﻿#pragma once

// Inner Fire 游戏引擎库
// 相机类
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个文件用于各种相机。根据《DirectX12游戏开发实战》中的工具类修改。
// 原作者：Frank Luna
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-20

#include <dx_core/dx_core.h>

namespace ifire {

enum class CameraType { NORMAL, SPHERICAL };

class SphericalCamera {
public:
  SphericalCamera();
  ~SphericalCamera() = default;

  void UpdateViewMatrix();
  XMMATRIX GetViewMatrix() const { return view_; }
  XMFLOAT3 GetPosition() const { return position_; }
  void OnMouseMove(WPARAM btn_state, float x, float y);
  void OnZoom(float move_x, float move_y);
  void OnRotate(float move_x, float move_y);

private:
  Spherical coord_;   // 使用球面坐标来表示相机
  XMFLOAT3 position_; // 相机位置
  XMMATRIX view_;
};

class Camera {
public:
  Camera();
  ~Camera();

  // Get/Set world camera position.
  DirectX::XMVECTOR GetPosition() const;
  DirectX::XMFLOAT3 GetPosition3f() const;
  void SetPosition(float x, float y, float z);
  void SetPosition(const DirectX::XMFLOAT3& v);

  // Get camera basis vectors.
  DirectX::XMVECTOR GetRight() const;
  DirectX::XMFLOAT3 GetRight3f() const;
  DirectX::XMVECTOR GetUp() const;
  DirectX::XMFLOAT3 GetUp3f() const;
  DirectX::XMVECTOR GetLook() const;
  DirectX::XMFLOAT3 GetLook3f() const;

  // Get frustum properties.
  float GetNearZ() const;
  float GetFarZ() const;
  float GetAspect() const;
  float GetFovY() const;
  float GetFovX() const;

  // Get near and far plane dimensions in view space coordinates.
  float GetNearWindowWidth() const;
  float GetNearWindowHeight() const;
  float GetFarWindowWidth() const;
  float GetFarWindowHeight() const;

  // Set frustum.
  void SetLens(float fovY, float aspect, float zn, float zf);

  // Define camera space via LookAt parameters.
  void LookAt(DirectX::FXMVECTOR pos, DirectX::FXMVECTOR target,
      DirectX::FXMVECTOR worldUp);
  void LookAt(const DirectX::XMFLOAT3& pos, const DirectX::XMFLOAT3& target,
      const DirectX::XMFLOAT3& up);

  // Get View/Proj matrices.
  DirectX::XMMATRIX GetView() const;
  DirectX::XMMATRIX GetProj() const;

  DirectX::XMFLOAT4X4 GetView4x4f() const;
  DirectX::XMFLOAT4X4 GetProj4x4f() const;

  // Strafe/Walk the camera a distance d.
  void Strafe(float d);
  void Walk(float d);

  // Rotate the camera.
  void Pitch(float angle);
  void RotateY(float angle);

  // After modifying camera position/orientation, call to rebuild the view
  // matrix.
  void UpdateViewMatrix();

private:
  // Camera coordinate system with coordinates relative to world space.
  DirectX::XMFLOAT3 mPosition = {0.0f, 0.0f, 0.0f};
  DirectX::XMFLOAT3 mRight = {1.0f, 0.0f, 0.0f};
  DirectX::XMFLOAT3 mUp = {0.0f, 1.0f, 0.0f};
  DirectX::XMFLOAT3 mLook = {0.0f, 0.0f, 1.0f};

  // Cache frustum properties.
  float mNearZ = 0.0f;
  float mFarZ = 0.0f;
  float mAspect = 0.0f;
  float mFovY = 0.0f;
  float mNearWindowHeight = 0.0f;
  float mFarWindowHeight = 0.0f;

  bool mViewDirty = true;

  // Cache View/Proj matrices.
  DirectX::XMFLOAT4X4 mView = IDENTITY_4X4;
  DirectX::XMFLOAT4X4 mProj = IDENTITY_4X4;
};

} // namespace ifire